package com.lightsaber.trade.cloth.service.programme;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springside.modules.orm.Page;

import com.lightsaber.trade.core.dao.BaseDao;
import com.lightsaber.trade.core.entity.AuditedObject;
import com.lightsaber.trade.core.entity.BaseEntity;
import com.lightsaber.trade.core.service.HppBaseServiceImpl;
import com.lightsaber.trade.cloth.dao.category.CategoryDao;
import com.lightsaber.trade.cloth.dao.category.SelectedPhraseOutDao;
import com.lightsaber.trade.cloth.dao.planning.CategoryNodeProgrameTempDao;
import com.lightsaber.trade.cloth.dao.planning.ProgrammePlanningItemDao;
import com.lightsaber.trade.cloth.dao.programme.CurriculumHourDao;
import com.lightsaber.trade.cloth.dao.programme.MpiOperatingCampusDao;
import com.lightsaber.trade.cloth.dao.programme.MpiProgrammeDao;
import com.lightsaber.trade.cloth.dao.programme.OfferingCampusDao;
import com.lightsaber.trade.cloth.dao.programme.ProgrammeCodeDao;
import com.lightsaber.trade.cloth.dao.programme.ProgrammeDao;
import com.lightsaber.trade.cloth.dao.programme.ProgrammeLeaderDao;
import com.lightsaber.trade.cloth.dao.programme.StreamDao;
import com.lightsaber.trade.cloth.dao.programme.SubjectGroupDao;
import com.lightsaber.trade.cloth.dao.programme.SubjectGroupItemDao;
import com.lightsaber.trade.cloth.entity.category.Category;
import com.lightsaber.trade.cloth.entity.category.CategoryLeaf;
import com.lightsaber.trade.cloth.entity.category.SelectedPhraseOut;
import com.lightsaber.trade.cloth.entity.programme.BaseProgramme;
import com.lightsaber.trade.cloth.entity.programme.CurriculumHour;
import com.lightsaber.trade.cloth.entity.programme.DurationType;
import com.lightsaber.trade.cloth.entity.programme.HkcaavqValidation;
import com.lightsaber.trade.cloth.entity.programme.InternalValidation;
import com.lightsaber.trade.cloth.entity.programme.MpiOperatingCampus;
import com.lightsaber.trade.cloth.entity.programme.MpiOperatingCampusId;
import com.lightsaber.trade.cloth.entity.programme.MpiProgramme;
import com.lightsaber.trade.cloth.entity.programme.MpiProgrammeId;
import com.lightsaber.trade.cloth.entity.programme.MpiProgrammeLeaderRight;
import com.lightsaber.trade.cloth.entity.programme.MpiProgrammeLeaderRightId;
import com.lightsaber.trade.cloth.entity.programme.OfferingCampus;
import com.lightsaber.trade.cloth.entity.programme.Programme;
import com.lightsaber.trade.cloth.entity.programme.ProgrammeCode;
import com.lightsaber.trade.cloth.entity.programme.ProgrammeLeader;
import com.lightsaber.trade.cloth.entity.programme.ProgrammePlanProjectionType;
import com.lightsaber.trade.cloth.entity.programme.ProgrammeStatus;
import com.lightsaber.trade.cloth.entity.programme.Stream;
import com.lightsaber.trade.cloth.entity.programme.SubjectGroup;
import com.lightsaber.trade.cloth.service.category.CategoryNodeService;
import com.lightsaber.trade.cloth.service.category.CategoryService;
import com.lightsaber.trade.cloth.web.category.CategoryAction;

/**
 * @author LiuJian
 * 
 */
@Service
@Transactional
public class ProgrammeManager extends HppBaseServiceImpl<Programme, String> {

    @Autowired
    ProgrammeDao programmeDao;
    @Autowired
    ProgrammePlanProjectionTypeManager programmePlanProjectionTypeManager;
    @Autowired
    CurriculumHourManager curriculumHourManager;
    @Autowired
    CategoryNodeProgrameTempDao categoryNodeProgrameTempDao;
    @Autowired
    SelectedPhraseOutDao selectedPhraseOutDao;

    @Autowired
    ProgrammePlanningItemDao programmePlanningItemDao;

    @Autowired
    CategoryNodeService nodeService;
    @Autowired
    CategoryService categoryService;

    @Autowired
    CategoryDao categoryDao;

    @Autowired
    private OfferingCampusDao offeringCampusDao;

    @Autowired
    private ProgrammeCodeDao programmeCodeDao;

    @Autowired
    private CurriculumHourDao curriculumHourDao;
    @Autowired
    private ProgrammeLeaderDao programmeLeaderDao;
    @Autowired
    private StreamDao streamDao;
    @Autowired
    private SubjectGroupDao subjectGroupDao;
    @Autowired
    private SubjectGroupItemDao subjectGroupItemDao;

    @Override
    protected BaseDao<Programme, String> getDao() {
        return programmeDao;
    }

    public long countByCodeAndYearAndVersion(final Programme programme) {
        return programmeDao.countByCodeAndYearAndVersion(programme);
    }

    public Page<Programme> findPage(final Programme programme, final Page<Programme> page) {
        return programmeDao.findPage(programme, page);
    }

    @Override
    public void save(final Programme programme) {

        if (StringUtils.isBlank(programme.getId())) {
            programme.setStatus(ProgrammeStatus.Created);

            super.save(programme);

            programmePlanProjectionTypeManager.onSaveNewProgramme(programme);
            curriculumHourManager.onSaveProgramme(programme);

            // save ProgrammeCode
            saveCode(programme);

            // update projectin type
            programmePlanProjectionTypeManager.changeFeederOnNewProgramme(programme);

        } else {

            Programme oldProgramme = get(programme.getId());

            /**
             * When edit programme, some fields can't be update: <br>
             * mapping to programme-input.jsp <br>
             * 
             * $("#programmeCode").attr("disabled",true);//1
             * $("#firstLaunchYear").attr("disabled",true);//2
             * $("#launchVersion").attr("disabled",true);//3
             * $("#isStreamOrProgramme").attr("disabled",true);//4
             * $("#foundingMode").attr("disabled",true);//5
             * $("#studyMode").attr("disabled",true);//6
             * $("#minimumnEntryLevel").attr("disabled",true);//7
             * $("#duration").attr("disabled",true);//8
             * $("#subjectGroupBox").attr("disabled",true);//9
             * $("#subjectGroupFeeder").attr("disabled",true);//10
             * $("#requirePlanning").attr("disabled",true);//11
             * $("#garType").attr("disabled",true);//12
             * $("#internalValidationPeriod").attr("disabled",true);//13
             * $("#cardExpiry").attr("disabled",true);//14
             * $("#approvalDate").attr("disabled",true);//15
             * $("#launchYear").attr("disabled",true);//16
             */

            BeanUtils.copyProperties(programme, oldProgramme, new String[] { BaseEntity.ID,
                    BaseEntity.VERSION, AuditedObject.CREATED_BY, AuditedObject.CREATED_DATE,
                    "code"/* 1 */, "firstLaunchYear"/* 2 */, "launchVersion"/* 3 */,
                    "streamOrProgramme"/* 4 */, "fundingMode"/* 5 */, "studyMode"/* 6 */,
                    "minimumnEntryLevel"/* 7 */, "duration"/* 8 */, "durationType"/* 8 */,
                    "requirePlanning"/* 11 */, "garType"/* 12 */,
                    "internalValidationPeriod"/* 13 */, "cardExpiry"/* 14 */,
                    "approvalDate"/* 15 */, "launchYear"/* 16 */, "revisionType"/* 17 */});

            // calculate the student card expire
            Integer duration = oldProgramme.getDuration();
            Integer cardExpiry = duration * 12;
            oldProgramme.setCardExpiry(cardExpiry);

            oldProgramme.setStatus(ProgrammeStatus.Updated);
            super.update(oldProgramme);
        }
    }

    private void saveCode(final Programme programme) {
        ProgrammeCode pCode = programmeCodeDao.getByCode(programme.getCode());
        if (pCode == null) {
            pCode = new ProgrammeCode();
            pCode.setCode(programme.getCode());
            programmeCodeDao.save(pCode);
        }
    }

    @Override
    public void delete(final String programmeId) {
        Programme programme = super.get(programmeId);

        programmePlanProjectionTypeManager.changeFeederOnDelProgramme(programme);

        selectedPhraseOutDao.deleteByProgramme(programme);

        categoryNodeProgrameTempDao.deleteByProgramme(programme);

        super.delete(programme);
    }

    public Map<String, String> checkCode(final String code, final String launchYear,
            final String launchVersion) {
        return programmeDao.checkCode(code, launchYear, launchVersion);
    }

    public boolean hasSameCodeAndLaunchYearAndLaunchVersion(final Programme programme) {
        List<Programme> matches = programmeDao.findByCodeAndLaunchYearAndLaunchVersion(
                programme.getCode(), programme.getLaunchYear(), programme.getLaunchVersion());

        for (Programme each : matches) {
            if (each.getId().equals(programme.getId())) {
                continue;
            } else {
                return true;
            }
        }

        return false;

    }

    public Page<Programme> viewResultSet(final String categoryId, final CategoryLeaf leaf,
            final Page<Programme> page) {

        Category category = categoryDao.get(categoryId);

        return programmeDao.viewResultSet(category, leaf, page);
    }

    /**
     * NO.1 method of Leaf's Programmes
     * <p>
     * 
     * Find programmes by plan year, bureau, founding mode, and attributes
     * 
     * @param categoryId
     * @param leafId
     * @return
     */
    public List<Programme> filterByAttributes(final String categoryId, final String leafId) {
        Category category = categoryService.findById(categoryId);
        CategoryLeaf leaf = nodeService.findLeafById(leafId);

        return programmeDao.filterByAttributes(category, leaf);
    }

    /**
     * NO.2 method of Leaf's Programmes
     * <p>
     * 
     * Find phrase out Programmes from Programmes List.
     * 
     * @param categoryId
     * @param leafId
     * @return
     */
    public List<Programme> filterPhraseOutFromProgrammes(final List<Programme> programmes) {

        List<Programme> phraseOuts = new ArrayList<Programme>();

        for (Programme programme : programmes) {
            phraseOuts.add(programme);
        }

        return phraseOuts;
    }

    /**
     * NO.3 method of Leaf's Programmes
     * <p>
     * 
     * Find programmes which in filtered by attributes but not in selected
     * phrase out table
     * 
     * @param categoryId
     * @param leafId
     * @return
     */
    public List<Programme> findProgrammesActualBelongToLeaf(final String categoryId,
            final String leafId) {
        List<Programme> programmes = this.filterByAttributes(categoryId, leafId);

        List<Programme> phraseOuts = this.filterPhraseOutFromProgrammes(programmes);

        CategoryLeaf leaf = nodeService.findLeafById(leafId);
        List<SelectedPhraseOut> selectedPhraseOuts = leaf.getSelectedPhraseOuts();
        Map<String, SelectedPhraseOut> idMap = new HashMap<String, SelectedPhraseOut>();
        for (SelectedPhraseOut selectedPhraseOut : selectedPhraseOuts) {
            idMap.put(selectedPhraseOut.getProgramme().getId(), selectedPhraseOut);
        }

        for (Programme phraseOut : phraseOuts) {
            if (!idMap.containsKey(phraseOut.getId())) {
                programmes.remove(phraseOut);
            }
        }

        return programmes;
    }

    public List<String> getAllDiscipline(final int exerciseYear, final String studyMode,
            final String campus, final String department) {
        return programmeDao.getAllDiscipline(exerciseYear, studyMode, campus, department);
    }

    public List<String> getDistinctDisciplineByCode(final int exerciseYear, final String code) {
        return programmeDao.getDistinctDisciplineByCode(exerciseYear, code);
    }

    public List<String> findByDiscipline(final String disciplineType,
            final String searchByInService, final Integer exerciseYear) {
        return programmeDao.findByDiscipline(disciplineType, searchByInService, exerciseYear);
    }

    public Integer getMaxDurationByDiscipline(final String discipline) {
        List<Integer> list = programmeDao.getMaxDurationByDiscipline(discipline);
        if (list.isEmpty()) {
            return null;
        } else {
            return list.get(0);
        }
    }

    public List<Programme> findByDisciplineAndCampusAndDep(final String discipline,
            final String operatingCampus, final String operatingUnit, final String studyMode) {
        return programmeDao.findByDisciplineAndCampusAndDep(discipline, operatingCampus,
                operatingUnit, studyMode);
    }

    public List<Programme> getFeederList() {
        return programmeDao.getFeederList();
    }

    public void saveCopy(final Programme programme, final String copyId) {
        programme.setStatus(ProgrammeStatus.Created);

        // programme.setSubjectGroupFeeder(subjectGroupFeeder);
        super.save(programme);
        Programme copyFrom = super.get(copyId);
        saveCode(programme);

        if (programme.getCode().equals(copyFrom.getCode())
                && programme.getDuration().equals(copyFrom.getDuration())) {
            // 1.---------------- Copy Leader ----------------
            copyLeader(programme, copyFrom);
            // 2.---------------- Copy Campus ----------------
            copyOfferingCampus(programme, copyFrom);
            // 3.---------------- Copy Curriculum Hour ----------------
            copyCurriculumHour(programme, copyFrom);

            // 4. ---------------- Copy Projection Type ----------------
            copyProjectionType(programme, copyFrom);
            if (this.isFeederOrReceiver(programme.getCode())) {
                programmePlanProjectionTypeManager.changeFeederOnNewProgramme(programme);

            }

            // 5. ---------------- Copy Stream ----------------
            copyStream(programme, copyFrom);

        } else if (!programme.getCode().equals(copyFrom.getCode())
                && programme.getDuration().equals(copyFrom.getDuration())) {
            // 1.---------------- Copy Leader ----------------
            copyLeader(programme, copyFrom);
            // 2.---------------- Copy Campus ----------------
            copyOfferingCampus(programme, copyFrom);
            // 3.---------------- Copy Curriculum Hour ----------------
            copyCurriculumHour(programme, copyFrom);

            // 4. ---------------- Copy Projection Type ----------------
            copyProjectionType(programme, copyFrom);
            if (this.isFeederOrReceiver(programme.getCode())) {
                programmePlanProjectionTypeManager.changeFeederOnNewProgramme(programme);

            }

        } else if (programme.getCode().equals(copyFrom.getCode())
                && !programme.getDuration().equals(copyFrom.getDuration())) {
            // 1.---------------- Copy Leader ----------------
            copyLeader(programme, copyFrom);
            // 2.---------------- Copy Campus ----------------
            copyOfferingCampus(programme, copyFrom);

            // 3.---------------- generate Curriculum Hour ----------------
            curriculumHourManager.onSaveProgramme(programme);

            // 4. ---------------- generate Projection Type ----------------
            programmePlanProjectionTypeManager.onSaveNewProgramme(programme);
            if (this.isFeederOrReceiver(programme.getCode())) {
                programmePlanProjectionTypeManager.changeFeederOnNewProgramme(programme);
            }

            // 5. ---------------- Copy Stream ----------------
            copyStream(programme, copyFrom);

        } else {

            // 1.---------------- Copy Leader ----------------
            copyLeader(programme, copyFrom);
            // 2.---------------- Copy Campus ----------------
            copyOfferingCampus(programme, copyFrom);

            // 3.---------------- generate Curriculum Hour ----------------
            curriculumHourManager.onSaveProgramme(programme);

            // 4. ---------------- generate Projection Type ----------------
            programmePlanProjectionTypeManager.onSaveNewProgramme(programme);
            if (this.isFeederOrReceiver(programme.getCode())) {
                programmePlanProjectionTypeManager.changeFeederOnNewProgramme(programme);
            }
        }

    }

    private void copyStream(Programme programme, Programme copyFrom) {
        List<Stream> streams = copyFrom.getStreams();
        for (Stream stream : streams) {

            Stream newStream = new Stream();
            BeanUtils.copyProperties(stream, newStream, new String[] { BaseEntity.ID,
                    BaseEntity.VERSION, AuditedObject.CREATED_BY, AuditedObject.CREATED_DATE,
                    "programme", "code", "duration", "durationType", "offeringCampusList",
                    "programmePlanProjectionTypes", "curriculumHours", "programmeLeaders" });

            newStream.setProgramme(programme);
            newStream.setCode(programme.getCode());
            newStream.setDuration(programme.getDuration());
            newStream.setDurationType(programme.getDurationType());

            streamDao.save(newStream);

            // 1.---------------- Copy Leader ----------------
            copyLeader(newStream, stream);
            // 2.---------------- Copy Campus ----------------
            copyOfferingCampus(newStream, stream);
            // 3.---------------- Copy Curriculum Hour ----------------
            copyCurriculumHour(newStream, stream);
            // 4.---------------- Copy Projection Type ----------------
            copyProjectionType(newStream, stream);
        }

    }

    private void copyProjectionType(final BaseProgramme programme, BaseProgramme copyFrom) {
        List<ProgrammePlanProjectionType> typeList = copyFrom.getProgrammePlanProjectionTypes();

        for (ProgrammePlanProjectionType projectType : typeList) {
            ProgrammePlanProjectionType newProjectType = new ProgrammePlanProjectionType();
            BeanUtils.copyProperties(projectType, newProjectType, new String[] { BaseEntity.ID,
                    BaseEntity.VERSION, AuditedObject.CREATED_BY, AuditedObject.CREATED_DATE,
                    "programme" });

            newProjectType.setProgramme(programme);
            programmePlanProjectionTypeManager.save(newProjectType);
        }
    }

    private void copyCurriculumHour(final BaseProgramme programme, BaseProgramme copyFrom) {
        List<CurriculumHour> curriculumHourList = copyFrom.getCurriculumHours();
        for (CurriculumHour curriculumHour : curriculumHourList) {
            CurriculumHour newcurriculumHour = new CurriculumHour();
            BeanUtils.copyProperties(curriculumHour, newcurriculumHour, new String[] {
                    BaseEntity.ID, BaseEntity.VERSION, AuditedObject.CREATED_BY,
                    AuditedObject.CREATED_DATE, "programme" });

            newcurriculumHour.setProgramme(programme);
            curriculumHourDao.save(newcurriculumHour);
        }
    }

    private void copyOfferingCampus(final BaseProgramme programme, BaseProgramme copyFrom) {
        List<OfferingCampus> offeringCampusList = copyFrom.getOfferingCampusList();
        for (OfferingCampus offeringCampus : offeringCampusList) {
            OfferingCampus newOfferingCampus = new OfferingCampus();

            BeanUtils.copyProperties(offeringCampus, newOfferingCampus, new String[] {
                    BaseEntity.ID, BaseEntity.VERSION, AuditedObject.CREATED_BY,
                    AuditedObject.CREATED_DATE, "programme" });

            newOfferingCampus.setProgramme(programme);
            offeringCampusDao.save(newOfferingCampus);
        }
    }

    private void copyLeader(final BaseProgramme programme, BaseProgramme copyFrom) {
        // 1.---------------- Copy Leader ----------------
        List<ProgrammeLeader> leaderList = copyFrom.getProgrammeLeaders();
        for (ProgrammeLeader leader : leaderList) {
            ProgrammeLeader newLeader = new ProgrammeLeader();
            BeanUtils.copyProperties(leader, newLeader, new String[] { BaseEntity.ID,
                    BaseEntity.VERSION, AuditedObject.CREATED_BY, AuditedObject.CREATED_DATE,
                    "programme" });

            newLeader.setProgramme(programme);
            programmeLeaderDao.save(newLeader);
        }
    }

    public boolean isFeederOrReceiver(final String code) {
        long groupCount = subjectGroupDao.countBy(code);
        long groupItemCount = subjectGroupItemDao.countBy(code);

        if (groupCount > 0 || groupItemCount > 0) {
            return true;
        } else {
            return false;
        }
    }

    public boolean hasStream(final String code) {
        List<Programme> programmes = programmeDao.findByCode(code);

        for (Programme programme : programmes) {

            if (programme.isRequireStream() || programme.getStreams().size() > 0) {
                return true;
            }
        }

        return false;

    }

    /* added by dj */
    private static final Logger log = LoggerFactory.getLogger(CategoryAction.class);

    @Autowired
    private MpiProgrammeLeaderRightSerivice leaderRighSerivice;

    @Autowired
    private MpiProgrammeDao mpiProgrammeDao;
    @Autowired
    private MpiOperatingCampusDao campusDao;

    /**
     * programme confirm logic
     * 
     * @param oldP
     * @throws Exception
     */
    public Map<String, String> confirm(Programme oldP) throws Exception {

        Map<String, String> exMap = new HashMap<String, String>();
        // get programme baic info
        // get programmeCode
        // get HKCAAVQ_VALID
        // get INTERNAL_VALID
        try {
            if (null == oldP) {
                exMap.put("info", "no Programme found");
                return exMap;
            }

            if (oldP.getStatus() == ProgrammeStatus.Confirmed) {
                exMap.put("info", "Proramme is confirmed before!");
                return exMap;
            }

            boolean isStream = oldP.getProgrammeKind().equals("Stream") ? true : false;

            if (isStream) {
                exMap.put("info", "could not confirm A single Stream");
                return exMap;
            }

            // Programme streams
            List<Stream> streams = oldP.getStreams();
            List<OfferingCampus> offeringCampuses = oldP.getOfferingCampusList();
            List<ProgrammeLeader> progLeaders = oldP.getProgrammeLeaders();

            if (null != offeringCampuses && offeringCampuses.isEmpty()) {
                exMap.put("info", "no Programme Campus found!");
                return exMap;
            }
            /*
             * if (null != progLeaders && progLeaders.isEmpty()) {
             * exMap.put("info", "no Programme Leader found!"); return exMap; }
             */

            MpiProgramme mpiProgrammeProg = new MpiProgramme();
            MpiProgrammeId mpiProgrammeIdProg = new MpiProgrammeId();

            Date approvalDate = new Date();
            ProgrammeCode progCodeProg = programmeCodeDao.getByCode(oldP.getCode());

            // if(isProgramme){
            mpiProgrammeProg.setApprovalDate(approvalDate);
            mpiProgrammeProg.setFundingMode(oldP.getFundingMode());

            List<SubjectGroup> groupsProg = progCodeProg.getSubjectGroups();

            if (null != groupsProg && !groupsProg.isEmpty()) {
                mpiProgrammeProg.setIsSubjectGroup("Y");
            } else {
                mpiProgrammeProg.setIsSubjectGroup("N");
            }

            mpiProgrammeProg.setProgDuration(oldP.getDuration());

            DurationType durationType = oldP.getDurationType();

            switch (durationType) {
            case Year:
                mpiProgrammeProg.setNormalDuration(oldP.getDuration() + "Y");
                break;
            case Month:
                mpiProgrammeProg.setNormalDuration(oldP.getDuration() + "M");
                break;
            case Day:
                mpiProgrammeProg.setNormalDuration(oldP.getDuration() + "D");
                break;
            default:
                break;
            }

            if (null != oldP.getDurationType()) {
                mpiProgrammeProg.setProgDurationUnit(oldP.getDurationType());
            }

            mpiProgrammeProg.setMainstream(oldP.getMainStream());
            mpiProgrammeProg.setProgType(oldP.getProgrammeType());
            // }

            // copy one programme
            Integer curricHourProg = 0;
            List<CurriculumHour> hoursProg = oldP.getCurriculumHours();
            if (hoursProg != null && !hoursProg.isEmpty()) {
                for (CurriculumHour h : hoursProg) {
                    if (h != null) {
                        if (h.getHours() != null) {
                            curricHourProg += h.getHours();
                        }
                    }
                }
            }

            List<HkcaavqValidation> hkcaavqValidationsProg = progCodeProg.getHkcaavqValidations();
            List<InternalValidation> internalValidationsProg = progCodeProg
                    .getInternalValidations();

            Date hkcaavqValidFmProg = null;
            Date hkcaavqValidToProg = null;

            Date internalValidFmProg = null;
            Date internalValidToProg = null;
            if (null != hkcaavqValidationsProg && !hkcaavqValidationsProg.isEmpty()) {
                HkcaavqValidation hkcaavqValidation = hkcaavqValidationsProg.get(0);
                hkcaavqValidFmProg = hkcaavqValidation.getValidationPeriodFrom();
                hkcaavqValidToProg = hkcaavqValidation.getValidationPeriodTo();
                mpiProgrammeProg.setProgArea(hkcaavqValidation.getAreaOfStudy());
                mpiProgrammeProg.setProgSubarea(hkcaavqValidation.getProgrammeArea());
            }
            if (null != internalValidationsProg && !internalValidationsProg.isEmpty()) {
                InternalValidation internalValidation = internalValidationsProg.get(0);
                internalValidFmProg = internalValidation.getValidityPeriodOfExpire();
                internalValidToProg = internalValidation.getValidityPeriodOfExpire();
            }

            mpiProgrammeIdProg.setCode(oldP.getCode());
            mpiProgrammeIdProg.setLaunchYear(oldP.getLaunchYear());
            mpiProgrammeIdProg.setLaunchVersion(oldP.getLaunchVersion());

            // mpiProgramme.setProgArea(oldProgramme.getProgArea());
            // mpiProgramme.setProgSubarea(oldProgramme.getProgSubarea());

            mpiProgrammeProg.setId(mpiProgrammeIdProg);

            mpiProgrammeProg.setHkcaavqValidFm(hkcaavqValidFmProg);
            mpiProgrammeProg.setHkcaavqValidTo(hkcaavqValidToProg);
            mpiProgrammeProg.setInternalValidFm(internalValidFmProg);
            mpiProgrammeProg.setInternalValidTo(internalValidToProg);

            mpiProgrammeProg.setProgStructLevel(oldP.getProgrammeKind());
            mpiProgrammeProg.setDiscipline(oldP.getDiscipline());
            mpiProgrammeProg.setProgBoard(oldP.getBoard());
            mpiProgrammeProg.setQfLevel(oldP.getQfLevel());
            mpiProgrammeProg.setGarType(oldP.getGarType());
            mpiProgrammeProg.setProgCode(oldP.getCode());

            mpiProgrammeProg.setTitle(oldP.getTitleLong());
            mpiProgrammeProg.setcTitle(oldP.getTitleZhLong());

            mpiProgrammeProg.setShortTitle(oldP.getTitleShort());
            mpiProgrammeProg.setCShortTitle(oldP.getTitleZhShort());

            mpiProgrammeProg.setDeliveryMode(oldP.getStudyMode());

            mpiProgrammeProg.setAwardLevel(oldP.getAward());
            mpiProgrammeProg.setEntryLevel(oldP.getMinimumnEntryLevel());

            // **
            // mpiProgrammeProg.setStudCardExpiryDuration(oldP.getCardExpiry().toString());

            mpiProgrammeProg.setCurricHr(new Long(curricHourProg));
            // mpiProgramme.setProgArea(oldProgramme.getProgArea());
            // mpiProgramme.setProgSubarea(oldProgramme.getProgSubarea());

            // mpiProgramme.setIsSubjectGroup(oldProgramme.getIsSubjectGroup());
            // mpiProgramme.setSubjectGroupMaster(oldProgramme.getSubjectGroupMaster());

            // mpiProgrammeProg.setCreatedBy("Accentrix");
            // mpiProgrammeProg.setLastUpdatedBy("Accentrix");
            // mpiProgrammeProg.setLastUpdatedDate(new Date());
            // mpiProgrammeProg.setCreatedDate(new Date());

            mpiProgrammeProg.updateAuditInfo();
            mpiProgrammeDao.save(mpiProgrammeProg);

            if (null != progLeaders && !progLeaders.isEmpty()) {
                MpiProgrammeLeaderRightId progLeaderId = null;
                MpiProgrammeLeaderRight leaderRight = null;
                for (ProgrammeLeader leader : progLeaders) {

                    progLeaderId = new MpiProgrammeLeaderRightId();

                    progLeaderId.setCna(leader.getEnglishName());
                    progLeaderId.setProgrammeCode(oldP.getCode());

                    leaderRight = new MpiProgrammeLeaderRight();

                    leaderRight.setId(progLeaderId);
                    leaderRight.setcName(leader.getChineseName());
                    leaderRight.setName(leader.getEnglishName());
                    if (leader.isLeader()) {
                        leaderRight.setIsProgrammeLeader("Y");
                    } else {
                        leaderRight.setIsProgrammeLeader("N");
                    }
                    if (leader.isLeader()) {
                        leaderRight.setRmk("Programme " + oldP.getCode() + " launch in "
                                + oldP.getLaunchYear() + "." + leader.getEnglishName()
                                + " is the Programme leader of " + oldP.getCode() + " in "
                                + oldP.getLaunchYear());
                    }

                    // leaderRight.setCreatedBy("Accentrix");
                    // leaderRight.setLastUpdatedBy("Accentrix");
                    // leaderRight.setLastUpdatedDate(new Date());
                    // leaderRight.setCreatedDate(new Date());

                    leaderRight.updateAuditInfo();

                    leaderRighSerivice.save(leaderRight);
                }
            }

            if (null != offeringCampuses && !offeringCampuses.isEmpty()) {

                for (OfferingCampus c : offeringCampuses) {
                    MpiOperatingCampusId id = new MpiOperatingCampusId();

                    id.setCode(oldP.getCode());
                    id.setLaunchVersion(oldP.getLaunchVersion());
                    id.setLaunchYear(oldP.getLaunchYear());

                    id.setOfferingCampus(c.getCampus());
                    id.setOfferingDept(c.getOfferingUnit());

                    id.setOperatingCampus(c.getOperatingCampus());
                    id.setOperatingDept(c.getOperatingUnit());

                    MpiOperatingCampus mpiCampus = new MpiOperatingCampus();

                    mpiCampus.setId(id);

                    // mpiCampus.setCreatedBy("Accentrix");
                    // mpiCampus.setLastUpdatedBy("Accentrix");
                    // mpiCampus.setLastUpdatedDate(new Date());
                    // mpiCampus.setCreatedDate(new Date());

                    mpiCampus.updateAuditInfo();
                    campusDao.save(mpiCampus);
                }
            }

            // Stream do not have streams
            // if (oldP.getProgrammeKind().equals("Stream")) {
            // return false;
            // }

            if (null != streams && !streams.isEmpty()) {
                for (Stream s : streams) {
                    MpiProgrammeId streamId = new MpiProgrammeId();
                    MpiProgramme mpiStream = new MpiProgramme();

                    streamId.setCode(s.getCode() + s.getStreamCode());
                    streamId.setLaunchVersion(oldP.getLaunchVersion());
                    streamId.setLaunchYear(oldP.getLaunchYear());

                    mpiStream.setId(streamId);

                    Integer curricHourStream = 0;
                    List<CurriculumHour> hoursStream = s.getCurriculumHours();
                    if (hoursStream != null && !hoursStream.isEmpty()) {
                        for (CurriculumHour h : hoursStream) {
                            if (h != null) {
                                if (h.getHours() != null) {
                                    curricHourStream += h.getHours();
                                }
                            }
                        }
                    }

                    mpiStream.setProgStructLevel(s.getProgrammeKind());
                    mpiStream.setDiscipline(oldP.getDiscipline());
                    mpiStream.setProgBoard(oldP.getBoard());
                    mpiStream.setQfLevel(oldP.getQfLevel());
                    mpiStream.setGarType(oldP.getGarType());
                    mpiStream.setProgCode(s.getCode());
                    mpiStream.setStreamCode(s.getStreamCode());
                    mpiStream.setTitle(s.getTitleLong());
                    mpiStream.setcTitle(s.getTitleZhLong());

                    mpiStream.setShortTitle(s.getTitleShort());
                    mpiStream.setCShortTitle(s.getTitleZhShort());

                    mpiStream.setDeliveryMode(oldP.getStudyMode());

                    mpiStream.setAwardLevel(oldP.getAward());
                    mpiStream.setEntryLevel(oldP.getMinimumnEntryLevel());

                    // **
                    // mpiProgrammeProg.setStudCardExpiryDuration(oldProgramme.getCardExpiry().toString());

                    mpiStream.setCurricHr(new Long(curricHourStream));

                    // mpiProgramme.setProgArea(oldProgramme.getProgArea());
                    // mpiProgramme.setProgSubarea(oldProgramme.getProgSubarea());

                    // mpiProgramme.setIsSubjectGroup(oldProgramme.getIsSubjectGroup());
                    // mpiProgramme.setSubjectGroupMaster(oldProgramme.getSubjectGroupMaster());

                    // mpiStream.setCreatedBy("Accentrix");
                    // mpiStream.setLastUpdatedBy("Accentrix");
                    // mpiStream.setLastUpdatedDate(new Date());
                    // mpiStream.setCreatedDate(new Date());
                    mpiStream.updateAuditInfo();
                    mpiProgrammeDao.save(mpiStream);

                    List<OfferingCampus> streamOfferingCampuses = s.getOfferingCampusList();
                    List<ProgrammeLeader> streamLeaders = s.getProgrammeLeaders();

                    if (null != streamOfferingCampuses && streamOfferingCampuses.isEmpty()) {
                        exMap.put("info", "no Stream Campus found!");
                        return exMap;
                    }
                    /*
                     * if (null != streamLeaders && streamLeaders.isEmpty()) {
                     * exMap.put("info", "no Programme Leader found!"); return
                     * exMap; }
                     */

                    if (null != streamOfferingCampuses && !streamOfferingCampuses.isEmpty()) {
                        for (OfferingCampus cStream : streamOfferingCampuses) {
                            MpiOperatingCampusId streamCampusId = new MpiOperatingCampusId();

                            streamCampusId.setCode(oldP.getCode() + "-" + s.getStreamCode());
                            streamCampusId.setLaunchVersion(oldP.getLaunchVersion());
                            streamCampusId.setLaunchYear(oldP.getLaunchYear());

                            streamCampusId.setOfferingCampus(cStream.getCampus());
                            streamCampusId.setOfferingDept(cStream.getOfferingUnit());

                            streamCampusId.setOperatingCampus(cStream.getOperatingCampus());
                            streamCampusId.setOperatingDept(cStream.getOperatingUnit());

                            MpiOperatingCampus streamMpiCampus = new MpiOperatingCampus();

                            streamMpiCampus.setId(streamCampusId);

                            // streamMpiCampus.setCreatedBy("Accentrix");
                            // streamMpiCampus.setLastUpdatedBy("Accentrix");
                            // streamMpiCampus.setLastUpdatedDate(new Date());
                            // streamMpiCampus.setCreatedDate(new Date());
                            streamMpiCampus.updateAuditInfo();
                            campusDao.save(streamMpiCampus);

                        }
                    }

                    if (null != streamLeaders && !streamLeaders.isEmpty()) {
                        MpiProgrammeLeaderRightId streamLeaderId = null;
                        MpiProgrammeLeaderRight leaderRightStream = null;
                        for (ProgrammeLeader streamLeader : streamLeaders) {
                            streamLeaderId = new MpiProgrammeLeaderRightId();
                            streamLeaderId.setCna(streamLeader.getEnglishName());
                            streamLeaderId.setProgrammeCode(oldP.getCode());

                            leaderRightStream = new MpiProgrammeLeaderRight();

                            leaderRightStream.setId(streamLeaderId);

                            leaderRightStream.setName(streamLeader.getEnglishName());
                            leaderRightStream.setcName(streamLeader.getChineseName());

                            if (streamLeader.isLeader()) {
                                leaderRightStream.setIsProgrammeLeader("Y");
                            } else {
                                leaderRightStream.setIsProgrammeLeader("N");
                            }
                            if (streamLeader.isLeader()) {
                                leaderRightStream.setRmk("Stream " + s.getStreamCode()
                                        + " launch in " + oldP.getLaunchYear() + "."
                                        + streamLeader.getEnglishName()
                                        + " is the Stream leader of " + s.getStreamCode() + " in "
                                        + oldP.getLaunchYear());
                            }

                            // leaderRightStream.setCreatedBy("Accentrix");
                            // leaderRightStream.setLastUpdatedBy("Accentrix");
                            // leaderRightStream.setLastUpdatedDate(new Date());
                            // leaderRightStream.setCreatedDate(new Date());

                            leaderRightStream.updateAuditInfo();
                            leaderRighSerivice.save(leaderRightStream);

                        }
                    }
                }
            }

            /* change programme status to confirm */
            oldP.setStatus(ProgrammeStatus.Confirmed);
            oldP.setApprovalDate(new Date());
            programmeDao.update(oldP);

        } catch (Exception e) {

            log.error(e.getMessage(), e);
            throw e;
        }
        return exMap;
    }

    public List<Object> getAllFirstLaunchYearList() {
        return programmeDao.getAllFirstLaunchYears();
    }

    public boolean programmePlanningItemExistsCode(final String programmeCode) {
        return programmePlanningItemDao.existsCode(programmeCode);
    }

    public List<Programme> findByLanchYear(final Integer lanchYear) {
        return programmeDao.findBy("launchYear", lanchYear);
    }

    public List<String> distinctCodeByLaunchYear(final Integer launchYear) {
        return programmeDao.distinctCodeByLaunchYear(launchYear);
    }

    public List<Programme> findByCodeAndCurrentYear(final String code, final Integer launchYear) {
        return programmeDao.findByCodeAndCurrentYear(code, launchYear);
    }

    public List<Programme> findByCodeAndLaunchYear(final String code, final Integer launchYear) {
        return programmeDao.findByCodeAndLaunchYear(code, launchYear);
    }

    public List<Programme> findByCodesAndLaunchYear(final String[] codes, final Integer launchYear) {
        return programmeDao.findByCodesAndLaunchYear(codes, launchYear);
    }
}
